﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace WCS.Infrastructure.Tools
{
    /// <summary>
    /// 加密工具类
    /// </summary>
    public static class EncryptionUtility
    {
        private static string _encryptionKey = "Vjg4ajjmFu+y0DxrHNq4bstfnKGzVuLc8erusnFtpm4=";
        private static string _encryptionIV = "8qfamyT7+wq7illlp6dJcw==";
        private static readonly string EncryptedPrefix = "[ENCRYPTED]";


        public class RSACryption
        {

            #region RSA签名
            /// <summary>
            /// RSA签名
            /// </summary>
            /// <param name="data"></param>
            /// <param name="password"></param>
            /// <param name="privateKey_path"></param>
            /// <returns></returns>
            public static string RSASign(string data, string password, string privateKey_path)
            {
                X509Certificate2 privateCert = new X509Certificate2(privateKey_path, password, X509KeyStorageFlags.Exportable);
                SHA256 sha256 = new SHA256CryptoServiceProvider();
                byte[] hashbytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
                /*          RSAPKCS1SignatureFormatter signe = new RSAPKCS1SignatureFormatter();
                            signe.SetKey(privateCert.PrivateKey); //设置签名用到的私钥 
                            signe.SetHashAlgorithm("SHA256"); //设置签名算法 
                            byte[] reslut = signe.CreateSignature(hashbytes);
                            string sign = Convert.ToBase64String(reslut);*/   //弃用,有商户反映报指定的算法无效.
                byte[] reslut2;
                using (RSA privateKey = privateCert.GetRSAPrivateKey())
                {
                    reslut2 = privateKey.SignHash(hashbytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
                }
                string sign = Convert.ToBase64String(reslut2);
                return sign;
            }

            #endregion


            #region RSA验签  
            /// <summary>
            /// RSA验签
            /// </summary>
            /// <param name="data"></param>
            /// <param name="sign"></param>
            /// <param name="publicKey_path"></param>
            /// <returns></returns>
            public static bool verifySign(string data, string sign, string publicKey_path)
            {
                byte[] reslut = Convert.FromBase64String(sign);
                X509Certificate2 x509 = new X509Certificate2(publicKey_path);
                RSACryptoServiceProvider oRSA4 = new RSACryptoServiceProvider();
                oRSA4.FromXmlString(x509.PublicKey.Key.ToXmlString(false));

                bool bVerify = oRSA4.VerifyData(Encoding.UTF8.GetBytes(data), "SHA256", reslut);
                return bVerify;
            }
            #endregion

            #region RSA加密
            /// <summary>
            /// RSA加密
            /// </summary>
            /// <param name="data"></param>
            /// <param name="publicKey_path"></param>
            /// <returns></returns>
            //public static string RSAEncrypt(string data, string publicKey_path)
            //{
            //    X509Certificate2 x509 = new X509Certificate2(publicKey_path);
            //    RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            //    key.FromXmlString(x509.PublicKey.Key.ToXmlString(false));

            //    byte[] reslut = key.Encrypt(Encoding.UTF8.GetBytes(data), false);
            //    return Convert.ToBase64String(reslut); ;
            //}
            #endregion

            #region RSA加密
            public static string RSAEncrypt(string data, string publicKey_path)
            {
                X509Certificate2 x509 = new X509Certificate2(publicKey_path);
                RSACryptoServiceProvider key = new RSACryptoServiceProvider();
                key.FromXmlString(x509.PublicKey.Key.ToXmlString(false));

                // 分段加密以支持长数据（PKCS#1 v1.5，最大块 = keySize/8 - 11）
                int keySizeBytes = key.KeySize / 8;
                int maxInputBlock = keySizeBytes - 11;
                byte[] inputBytes = Encoding.UTF8.GetBytes(data);

                using (var ms = new MemoryStream())
                {
                    int offset = 0;
                    while (offset < inputBytes.Length)
                    {
                        int blockSize = Math.Min(maxInputBlock, inputBytes.Length - offset);
                        byte[] block = new byte[blockSize];
                        Buffer.BlockCopy(inputBytes, offset, block, 0, blockSize);

                        byte[] encryptedBlock = key.Encrypt(block, false); // false => PKCS#1 v1.5
                        ms.Write(encryptedBlock, 0, encryptedBlock.Length);

                        offset += blockSize;
                    }

                    return Convert.ToBase64String(ms.ToArray());
                }

            }
            #endregion
        }
        /// <summary>
        /// 初始化加密密钥
        /// 应在应用启动时调用一次
        /// </summary>
        public static void Initialize(string encryptionKey, string encryptionIV)
        {
            _encryptionKey = encryptionKey;
            _encryptionIV = encryptionIV;
        }

        /// <summary>
        /// 检查是否为加密值
        /// </summary>
        public static bool IsEncrypted(string value)
        {
            return !string.IsNullOrEmpty(value) &&
                   value.StartsWith(EncryptedPrefix, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// 解密值
        /// 如果不是加密值，直接返回原值
        /// </summary>
        public static string Decrypt(string value)
        {
            if (!IsEncrypted(value))
                return value;

            if (string.IsNullOrEmpty(_encryptionKey) || string.IsNullOrEmpty(_encryptionIV))
                throw new InvalidOperationException("加密工具未初始化，请先调用Initialize方法");

            try
            {
                // 移除前缀
                var encryptedValue = value.Substring(EncryptedPrefix.Length);

                // 解密
                using var aes = Aes.Create();
                aes.Key = Convert.FromBase64String(_encryptionKey);
                aes.IV = Convert.FromBase64String(_encryptionIV);

                using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
                using var memoryStream = new MemoryStream(Convert.FromBase64String(encryptedValue));
                using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
                using var streamReader = new StreamReader(cryptoStream);

                return streamReader.ReadToEnd();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"解密失败: {ex.Message}");
                return value; // 解密失败时返回原值
            }
        }

        /// <summary>
        /// 加密值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string Encrypt(string value)
        {
            return Encrypt(value, _encryptionKey, _encryptionIV);
        }

        /// <summary>
        /// 加密值
        /// 添加特定前缀以便识别
        /// </summary>
        public static string Encrypt(string value, string encryptionKey, string encryptionIV)
        {
            if (string.IsNullOrEmpty(value))
                return value;

            try
            {
                using var aes = Aes.Create();
                aes.Key = Convert.FromBase64String(encryptionKey);
                aes.IV = Convert.FromBase64String(encryptionIV);

                using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                using var memoryStream = new MemoryStream();
                using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
                using var streamWriter = new StreamWriter(cryptoStream);

                streamWriter.Write(value);
                streamWriter.Flush();
                cryptoStream.FlushFinalBlock();

                return EncryptedPrefix + Convert.ToBase64String(memoryStream.ToArray());
            }
            catch (Exception ex)
            {
                Console.WriteLine($"加密失败: {ex.Message}");
                return value; // 加密失败时返回原值
            }
        }

        /// <summary>
        /// 生成新的加密密钥和IV
        /// </summary>
        public static (string Key, string IV) GenerateNewKey()
        {
            using var aes = Aes.Create();
            aes.GenerateKey();
            aes.GenerateIV();

            return (Convert.ToBase64String(aes.Key), Convert.ToBase64String(aes.IV));
        }

        /// <summary>
        /// PBKDF2哈希密码存储
        /// </summary>
        /// <param name="password"></param>
        /// <returns></returns>
        public static string HashPassword(string password)
        {
            // 生成随机盐（16字节）
            byte[] salt = new byte[16];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(salt);

            // 使用PBKDF2派生密钥（32字节，256位）
            //var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations: 100000, hashAlgorithm: HashAlgorithmName.SHA256);//推荐显式指定更安全的算法（如HMAC-SHA256）
            using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations: 100000);//根据硬件升级调整（如200,000次）
            byte[] hash = pbkdf2.GetBytes(32);// 生成32字节（256位）哈希


            // 合并盐和哈希值（存储时需同时保存盐）
            byte[] hashWithSalt = new byte[48];
            Buffer.BlockCopy(salt, 0, hashWithSalt, 0, 16);// 前16字节存盐
            Buffer.BlockCopy(hash, 0, hashWithSalt, 16, 32);// 后32字节存哈希
            return Convert.ToBase64String(hashWithSalt);// Base64编码 转为可打印字符串
        }

        /// <summary>
        /// 验证密码
        /// </summary>
        /// <param name="inputPassword">输入的密码</param>
        /// <param name="storedHash">存储的加密密码</param>
        /// <returns></returns>
        public static bool VerifyPassword(string inputPassword, string storedHash)
        {
            // 从存储的哈希值中分离盐和哈希
            byte[] hashWithSalt = Convert.FromBase64String(storedHash);
            byte[] salt = new byte[16];
            Buffer.BlockCopy(hashWithSalt, 0, salt, 0, 16);

            // 使用相同的盐重新计算哈希
            using var pbkdf2 = new Rfc2898DeriveBytes(inputPassword, salt, iterations: 100000);
            byte[] computedHash = pbkdf2.GetBytes(32);

            // 比较计算出的哈希与存储的哈希是否一致
            for (int i = 0; i < 32; i++)
            {
                if (computedHash[i] != hashWithSalt[i + 16])
                {
                    return false;
                }
            }
            return true;
        }

    }
}
